序列化和反序列化是將物件(Object)狀態持久保存的重要過程,Java早在1.1以來就提供了Serializable
介面以實現物件的序列化機制。
序列化是將記憶體中的實體物件(Object)轉換為位元流(byte stream)、JSON String,或其他格式,以便能夠儲存於永久媒體(如硬碟),或與外部系統進行資料交換的過程。
這樣的處理,允許我們在未來可以透過反序列化(Deserialize)從中讀取該物件,並在記憶體中重建,從而恢復其先前的狀態。
一般我們在Java的開發過程中提到的序列化,多半是指將物件轉成byte stream。
實現物件序列化非常簡單,只需讓類別(Class)實作 java.io.Serializable 介面,再透過OutputStream輸出即可。
例如以下程式碼,可將Class Person的實例: person
轉成byte string輸出到外部檔案:person.serial
Class Person
public class Person implements Serializable {
private static final long serialVersionUID = 1L; // 明確版本控制
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
...
Person person = new Person("John", 30);
// 將物件序列化並輸出到檔案
try (FileOutputStream fileOut = new FileOutputStream("person.serial");
ObjectOutputStream objectOut = new ObjectOutputStream(fileOut)) {
// 寫入物件到檔案中
objectOut.writeObject(person);
...
person.serial
這個檔案轉回Person型態的物過時:...
try (FileInputStream fileIn = new FileInputStream("person.ser");
ObjectInputStream objectIn = new ObjectInputStream(fileIn)) {
// 讀取並轉換回 Person 物件
Person person = (Person) objectIn.readObject();
System.out.println("反序列化的物件: " + person);
...
總結來說,序列化和反序列化使得Java物件能夠在不同的儲存格式和傳輸環境中持久保存,並且能夠在需要時恢復原本的物件狀態,這在資料存取與系統間溝通時尤為重要。
com.fasterxml.jackson.databind.ObjectMapper
來進行Deserialize,例如以下片段:OpenAIResponse openAIResponse = objectMapper.readValue(jsonString, OpenAIResponse.class);
public class OpenAIResponse {
public List<MessageContent> messageContent;
}
這個Class 只包含了一個List,並且看到角括號<>
中指定的型態是MessageContent,這代表OpenAI回應的內容中,包含了JSON陣列(JSON Array),而Array中每個元素的結構則是在MessageContent這個class中定義:
Class MessageContent
package com.cancerpio.nextpagelinebotserver.model;
...
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "about")
@JsonSubTypes({
@Type(value = MessageContentTrainingLog.class, name = RecordType.TRAININGRECORDS),
@Type(value = MessageContentDiet.class, name = RecordType.DIET)
public abstract class MessageContent {
}
MessageContentDiet.class
{
"messageContent": [
{
"about": "Diet",
"calories": 250,
"protein": 35,
"fat": 10
}
]
}
package com.cancerpio.nextpagelinebotserver.model;
public class MessageContentDiet extends MessageContent {
public String about;
public Integer calories;
public Integer protein;
public Integer fat;
}
MessageContentTrainingLog.class
{
"messageContent": [
{
"about": "TrainingRecords",
"action": "Deadlift",
"actionType": "Weight lifting",
"weight": 140,
"repetition": 1,
"set": 5,
"percentagOfRepetitionMaximum": null,
"duration": 300,
"feeling": "超累",
"advice": "適當休息,注意保護腰部",
"date": "today"
}
]
}
package com.cancerpio.nextpagelinebotserver.model;
import org.springframework.data.mongodb.core.mapping.Document;
@Document("action_record")
public class MessageContentTrainingLog extends MessageContent {
public String userId;
public String action;
public String actionType;
public Integer weight;
public Integer repetition;
public Integer set;
public Integer percentagOfRepetitionMaximum;
public String duration;
public String feeling;
public String advice;
public String date;
}
以上就是序列化和反序列化的原理,在我們的程式中,有些地方是直接使用ObjectMapper進行反序列化,而在其他地方,則透過指定序列化方式和自行定義的Class,讓Spring Boot在背後自動執行這項任務。這部分的細節會在Kafka Consumer的實作見到。